package org.unidata.mdm.rest.v1.data.service.records;

import java.util.Objects;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.unidata.mdm.core.type.timeline.Timeline;
import org.unidata.mdm.data.context.DeleteRequestContext;
import org.unidata.mdm.data.context.GetRecordTimelineRequestContext;
import org.unidata.mdm.data.context.GetRequestContext;
import org.unidata.mdm.data.context.SplitRecordRequestContext;
import org.unidata.mdm.data.dto.DeleteRecordDTO;
import org.unidata.mdm.data.dto.GetRecordDTO;
import org.unidata.mdm.data.dto.SplitRecordsDTO;
import org.unidata.mdm.data.type.data.EtalonRecord;
import org.unidata.mdm.data.type.data.OriginRecord;
import org.unidata.mdm.data.type.keys.RecordEtalonKey;
import org.unidata.mdm.data.type.keys.RecordKeys;
import org.unidata.mdm.rest.system.ro.DetailedErrorResponseRO;
import org.unidata.mdm.rest.system.util.RestConstants;
import org.unidata.mdm.rest.v1.data.converter.DataRecordEtalonConverter;
import org.unidata.mdm.rest.v1.data.converter.RecordKeysConverter;
import org.unidata.mdm.rest.v1.data.converter.TimelineToTimelineROConverter;
import org.unidata.mdm.rest.v1.data.ro.keys.RecordExternalIdRO;
import org.unidata.mdm.rest.v1.data.ro.records.DeleteRecordRequestRO;
import org.unidata.mdm.rest.v1.data.ro.records.DeleteRecordResultRO;
import org.unidata.mdm.rest.v1.data.ro.records.DetachOriginRequestRO;
import org.unidata.mdm.rest.v1.data.ro.records.DetachOriginResultRO;
import org.unidata.mdm.rest.v1.data.ro.records.FetchKeysRequestRO;
import org.unidata.mdm.rest.v1.data.ro.records.FetchKeysResultRO;
import org.unidata.mdm.rest.v1.data.ro.records.FilterByCriteriaRequestRO;
import org.unidata.mdm.rest.v1.data.ro.records.FilterByCriteriaResultRO;
import org.unidata.mdm.rest.v1.data.ro.records.GetRecordRequestRO;
import org.unidata.mdm.rest.v1.data.ro.records.GetRecordResultRO;
import org.unidata.mdm.rest.v1.data.ro.records.GetTimelineRequestRO;
import org.unidata.mdm.rest.v1.data.ro.records.GetTimelineResultRO;
import org.unidata.mdm.rest.v1.data.ro.records.ReindexRecordRequestRO;
import org.unidata.mdm.rest.v1.data.ro.records.UpsertRecordResultRO;
import org.unidata.mdm.rest.v1.data.ro.records.UpsertRequestRO;
import org.unidata.mdm.rest.v1.data.service.AbstractDataRestService;
import org.unidata.mdm.system.type.runtime.MeasurementContextName;
import org.unidata.mdm.system.type.runtime.MeasurementPoint;
import org.unidata.mdm.system.util.ConvertUtils;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;

/**
 * Service for working with entities.
 * Provides read, write and delete operation
 *
 * @author Alexandr Serov
 * @since 07.10.2020
 **/
@Path("records")
public class DataRecordsRestService extends AbstractDataRestService {

    private static final String RECORDS_TAG = "records";

    /**
     * Default value for delete cascade.
     */
    private static final boolean DEFAULT_DELETE_CASCADE_VALUE = true;

    private boolean deleteCascadeValue = DEFAULT_DELETE_CASCADE_VALUE;

    @GET
    @Path("/{" + RestConstants.DATA_PARAM_NAME + "}/{" + RestConstants.DATA_PARAM_ID + "}")
    @Operation(description = "Get entity by id", method = GET_REQUEST, responses = {
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = GetRecordResultRO.class)), responseCode = "200")
    }, tags = RECORDS_TAG)
    public GetRecordResultRO entityById(
        @Parameter(description = "Entity name", in = ParameterIn.PATH) @PathParam(RestConstants.DATA_PARAM_NAME) String entityName,
        @Parameter(description = "Entity ID", in = ParameterIn.PATH) @PathParam(RestConstants.DATA_PARAM_ID) String id,
        @Parameter(description = "Draft id. Optional.", in = ParameterIn.QUERY)  @QueryParam(RestConstants.QUERY_PARAM_DRAFT_ID) @DefaultValue("0") long draftId,
        @Parameter(description = "Timeline date", in = ParameterIn.QUERY) @QueryParam(RestConstants.DATA_PARAM_DATE) String dateAsString) {
        GetRecordRequestRO req = new GetRecordRequestRO();
        req.setEntityName(entityName);
        req.setEtalonId(id);
        req.setTimelineDate(ConvertUtils.string2LocalDateTime(dateAsString));
        req.setDraftId(draftId);
        return executeGetEntity(req);
    }

    @POST
    @Path("/")
    @Produces({MediaType.APPLICATION_JSON})
    @Operation(
        description = "Get record by query",
        method = HttpMethod.POST,
        requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = GetRecordRequestRO.class)), description = "Get entity by query"),
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = GetRecordResultRO.class)), responseCode = "200")
        }, tags = RECORDS_TAG)
    public GetRecordResultRO entityByQuery(GetRecordRequestRO query) {
        return executeGetEntity(query);
    }

    @POST
    @Path("/keys")
    @Produces({MediaType.APPLICATION_JSON})
    @Operation(description = "Fetch keys by request", responses = {
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = FetchKeysResultRO.class)), responseCode = "200")
    }, tags = RECORDS_TAG)
    public FetchKeysResultRO fetchKeys(FetchKeysRequestRO query) {
        return executeFetchKeys(query);
    }

    /**
     * Gets the etalon id for an external id.
     *
     * @param externalId external ID
     * @param sourceSystem source system
     * @param entityName entity name
     * @return response
     */
    @GET
    @Path("/keys/" + RestConstants.PATH_PARAM_EXTERNAL
        + "/{" + RestConstants.DATA_PARAM_EXT_ID + "}"
        + "/{" + RestConstants.DATA_PARAM_SOURCE_SYSTEM + "}"
        + "/{" + RestConstants.DATA_PARAM_NAME + "}")
    @Operation(description = "Gets the etalon id for an external id.", responses = {
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = FetchKeysResultRO.class)), responseCode = "200")
    }, tags = RECORDS_TAG)
    public FetchKeysResultRO fetchKeysByExternalId(
        @Parameter(description = "Entity external keys", in = ParameterIn.PATH) @PathParam(RestConstants.DATA_PARAM_EXT_ID) String externalId,
        @Parameter(description = "Source system", in = ParameterIn.PATH) @PathParam(RestConstants.DATA_PARAM_SOURCE_SYSTEM) String sourceSystem,
        @Parameter(description = "Entity name", in = ParameterIn.PATH) @PathParam(RestConstants.DATA_PARAM_NAME) String entityName) {
        FetchKeysRequestRO req = new FetchKeysRequestRO();
        req.setEntityName(entityName);
        req.setExternalId(new RecordExternalIdRO(externalId, sourceSystem));
        return executeFetchKeys(req);
    }

    @POST
    @Path("/upsert")
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    @Operation(
        description = "Create or update entity",
        method = POST_REQUEST,
        requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = UpsertRequestRO.class)), description = "Upsert request"),
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = UpsertRecordResultRO.class)), responseCode = "200")
        }, tags = RECORDS_TAG
    )
    public UpsertRecordResultRO upsertEntity(UpsertRequestRO input) {
        return executeUpsert(input);
    }

    @POST
    @Path("/reindex")
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    @Operation(
        description = "Reindex entity",
        method = POST_REQUEST,
        requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ReindexRecordRequestRO.class)), description = "Reindex query"),
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
        }, tags = RECORDS_TAG
    )
    public boolean reindex(ReindexRecordRequestRO reindexQuery) {
        return executeReindex(reindexQuery);
    }

    @DELETE
    @Path("/delete/{" + RestConstants.DATA_PARAM_NAME + "}/{" + RestConstants.DATA_PARAM_ID + "}")
    @Operation(
        description = "Delete entity by id",
        method = DELETE_REQUEST,
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DeleteRecordResultRO.class)), responseCode = "200")
        }, tags = RECORDS_TAG
    )
    public DeleteRecordResultRO deleteEntity(
        @Parameter(description = "Entity name", in = ParameterIn.PATH) @PathParam(RestConstants.DATA_PARAM_NAME) String entityName,
        @Parameter(description = "Etalon ID", in = ParameterIn.PATH) @PathParam(RestConstants.DATA_PARAM_ID) String id,
        @Parameter(description = "Draft id. Optional.", in = ParameterIn.QUERY)  @QueryParam(RestConstants.QUERY_PARAM_DRAFT_ID) @DefaultValue("0") long draftId,
        @Parameter(description = "Inactivate etalon (soft-delete record)", in = ParameterIn.QUERY) @QueryParam(RestConstants.QUERY_PARAM_INACTIVATE_ETALON) Boolean inactivateEtalon,
        @Parameter(description = "Inactivate period", in = ParameterIn.QUERY) @QueryParam(RestConstants.QUERY_PARAM_INACTIVATE_PERIOD) Boolean inactivatePeriod,
        @Parameter(description = "Wipe record", in = ParameterIn.QUERY) @QueryParam(RestConstants.QUERY_PARAM_WIPE) Boolean wipe,
        @Parameter(description = "Period begin.", in = ParameterIn.QUERY) @QueryParam(VALID_FROM_PARAM) @DefaultValue("") String validFrom,
        @Parameter(description = "Period end.", in = ParameterIn.QUERY) @QueryParam(VALID_TO_PARAM) @DefaultValue("") String validTo) {

        DeleteRecordRequestRO req = new DeleteRecordRequestRO();

        req.setEntityName(entityName);
        req.setEtalonId(id);
        req.setValidFrom(StringUtils.isBlank(validFrom) ? null : ConvertUtils.string2LocalDateTime(validFrom));
        req.setValidTo(StringUtils.isBlank(validTo) ? null : ConvertUtils.string2LocalDateTime(validTo));
        req.setWipe(wipe);
        req.setInactivateEtalon(inactivateEtalon);
        req.setInactivatePeriod(inactivatePeriod);
        req.setDraftId(draftId);

        return executeDelete(req);
    }

    @POST
    @Path("/delete")
    @Operation(
        description = "Delete entity",
        method = POST_REQUEST,
        requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = DeleteRecordRequestRO.class)), description = "Delete request"),
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DeleteRecordResultRO.class)), responseCode = "200")
        }, tags = RECORDS_TAG
    )
    public DeleteRecordResultRO deleteEntity(DeleteRecordRequestRO request) {
        return executeDelete(request);
    }


    @POST
    @Path("/filter-by-criteria/")
    @Operation(description = "Filters the specified records by criteria and returns matching ones", responses = {
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = FilterByCriteriaResultRO.class)), responseCode = "200")
    }, tags = RECORDS_TAG)
    public FilterByCriteriaResultRO filterByCriteria(FilterByCriteriaRequestRO filterByCriteriaRequest) {
        return executeFilterByCriteria(filterByCriteriaRequest);
    }


    /**
     * Detach origin record from current etalon record.
     *
     * @param req Detach request
     */
    @POST
    @Path("/detach-origin")
    @Operation(description = "Detach origin record from current etalon record.", responses = {
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetachOriginResultRO.class)), responseCode = "200")
    }, tags = RECORDS_TAG)
    public DetachOriginResultRO detachOrigin(DetachOriginRequestRO req) {
        return executeDetachOrigin(req);
    }

    /**
     * Gets the time line for an etalon.
     *
     * @param etalonId the etalon ID
     * @return response
     */
    @GET
    @Path("/{" + RestConstants.DATA_PARAM_NAME + "}/{" + RestConstants.PATH_PARAM_TIMELINE + "}/{" + RestConstants.DATA_PARAM_ID + "}")
    @Operation(description = "Gets the time line for an etalon.", responses = {
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = GetTimelineResultRO.class)), responseCode = "200")
    }, tags = RECORDS_TAG)
    public GetTimelineResultRO recordsTimeline(@Parameter(description = "Entity name", in = ParameterIn.PATH)
                                               @PathParam(RestConstants.DATA_PARAM_NAME) String entityName,
                                               @Parameter(description = "Etalon id", in = ParameterIn.PATH)
                                               @PathParam(RestConstants.DATA_PARAM_ID) String etalonId,
                                               @Parameter(description = "Include draft versions", in = ParameterIn.QUERY)
                                               @QueryParam(RestConstants.DATA_PARAM_INCLUDE_DRAFTS) boolean includeDraft,
                                               @Parameter(description = "Draft id. Optional.", in = ParameterIn.QUERY)
                                               @QueryParam(RestConstants.QUERY_PARAM_DRAFT_ID) @DefaultValue("0") long draftId) {
        GetTimelineRequestRO query = new GetTimelineRequestRO();
        query.setEtalonId(etalonId);
        query.setIncludeDrafts(includeDraft);
        query.setEntityName(entityName);
        query.setDraftId(draftId);
        return executeGetTimelines(query);
    }

    // Executions

    private GetRecordResultRO executeGetEntity(GetRecordRequestRO query) {
        Objects.requireNonNull(query, "GetEntityQueryRO can't be null");
        MeasurementPoint.init(MeasurementContextName.MEASURE_UI_GET);
        MeasurementPoint.start();
        try {
            GetRecordDTO queryResult = dataRecordsService.getRecord(
                GetRequestContext.builder()
                    .etalonKey(query.getEtalonId())
                    .entityName(query.getEntityName())
                    .externalId(externalIdValue(query))
                    .sourceSystem(sourceSystemValue(query))
                    .lsn(lsnValue(query))
                    .shard(shardValue(query))
                    .forOperationId(query.getOperationId())
                    .includeInactive(query.isIncludeInactive())
                    .draftId(query.getDraftId())
                    .diffToDraft(query.isDiffToDraft())
                    .diffToPrevious(query.isDiffToPrevious())
                    .forDate(ConvertUtils.localDateTime2Date(query.getTimelineDate()))
                    .build());
            GetRecordResultRO result = new GetRecordResultRO();
            result.setRecordKeys(RecordKeysConverter.to(queryResult.getRecordKeys()));
            EtalonRecord etalonRecord = queryResult.getEtalon();
            if (etalonRecord != null) {
                result.setRecord(DataRecordEtalonConverter.to(etalonRecord, queryResult.getRecordKeys()));
            }
            return result;
        } finally {
            MeasurementPoint.stop();
        }
    }

    private GetTimelineResultRO executeGetTimelines(GetTimelineRequestRO query) {
        Timeline<OriginRecord> timeline = dataRecordsService.loadTimeline(GetRecordTimelineRequestContext.builder()
            .etalonKey(query.getEtalonId())
            .externalId(externalIdValue(query))
            .sourceSystem(sourceSystemValue(query))
            .lsn(lsnValue(query))
            .shard(shardValue(query))
            .draftId(query.getDraftId())
            .fetchData(false)
            .build());
        GetTimelineResultRO result = new GetTimelineResultRO();
        result.setTimeline(TimelineToTimelineROConverter.convert(timeline));
        return result;
    }

    private boolean executeReindex(ReindexRecordRequestRO reindexQuery) {
        Objects.requireNonNull(reindexQuery, "Reindex query can't be null");
        MeasurementPoint.init(MeasurementContextName.MEASURE_STEP_REINDEX);
        MeasurementPoint.start();
        try {
            return dataRecordsService.reindexEtalon(GetRequestContext.builder()
                .etalonKey(reindexQuery.getEtalonId())
                .entityName(reindexQuery.getEntityName())
                .externalId(externalIdValue(reindexQuery))
                .sourceSystem(sourceSystemValue(reindexQuery))
                .lsn(lsnValue(reindexQuery))
                .shard(shardValue(reindexQuery))
                .build());
        } finally {
            MeasurementPoint.stop();
        }
    }

    private DeleteRecordResultRO executeDelete(DeleteRecordRequestRO deleteQuery) {
        Objects.requireNonNull(deleteQuery, "DeleteEntityRequestRO can't be null");
        MeasurementPoint.init(MeasurementContextName.MEASURE_UI_DELETE_BY_ETALON);
        MeasurementPoint.start();
        try {
            DeleteRecordDTO deleteResult = dataRecordsService.deleteRecord(DeleteRequestContext.builder()
                .etalonKey(deleteQuery.getEtalonId())
                .entityName(deleteQuery.getEntityName())
                .externalId(externalIdValue(deleteQuery))
                .sourceSystem(sourceSystemValue(deleteQuery))
                .lsn(lsnValue(deleteQuery))
                .shard(shardValue(deleteQuery))
                .inactivateEtalon(BooleanUtils.toBoolean(deleteQuery.getInactivateEtalon()))
                .inactivateOrigin(BooleanUtils.toBoolean(deleteQuery.getInactivateOrigin()))
                .inactivatePeriod(BooleanUtils.toBoolean(deleteQuery.getInactivatePeriod()))
                .wipe(BooleanUtils.toBoolean(deleteQuery.getWipe()))
                .record(DataRecordEtalonConverter.from(deleteQuery.getDataRecord()))
                .validFrom(ConvertUtils.localDateTime2Date(deleteQuery.getValidFrom()))
                .validTo(ConvertUtils.localDateTime2Date(deleteQuery.getValidTo()))
                .draftId(deleteQuery.getDraftId())
                .cascade(deleteCascadeValue)
                .build());
            DeleteRecordResultRO result = new DeleteRecordResultRO();
            result.setRecordKeys(RecordKeysConverter.to(deleteResult.getRecordKeys()));
            RecordEtalonKey key = deleteResult.getEtalonKey();
            if (key != null) {
                result.setKey(key.getId());
            }
            result.setDetails(errorsToDetails(deleteResult.getErrors()));
            return result;
        } finally {
            MeasurementPoint.stop();
        }
    }

    private FetchKeysResultRO executeFetchKeys(FetchKeysRequestRO req) {

        Objects.requireNonNull(req, "FetchKeysRequestRO can't be null");
        MeasurementPoint.init(MeasurementContextName.MEASURE_UI_GET);
        MeasurementPoint.start();
        try {

            RecordKeys keys = dataRecordsService.identify(GetRequestContext.builder()
                .etalonKey(req.getEtalonId())
                .entityName(req.getEntityName())
                .sourceSystem(sourceSystemValue(req))
                .externalId(externalIdValue(req))
                .lsn(lsnValue(req))
                .shard(shardValue(req))
                .build());

            FetchKeysResultRO result = new FetchKeysResultRO();
            result.setKeys(RecordKeysConverter.to(keys));
            return result;
        } finally {
            MeasurementPoint.stop();
        }
    }

    private FilterByCriteriaResultRO executeFilterByCriteria(FilterByCriteriaRequestRO req) {
        Objects.requireNonNull(req, "FilterByCriteriaRequestRO can't be null");
        MeasurementPoint.init(MeasurementContextName.FILTER_BY_CRITERIA);
        MeasurementPoint.start();
        try {
            return new FilterByCriteriaResultRO(dataRecordsService.selectCovered(
                req.getEtalonIds(),
                req.getValidFrom(),
                req.getValidTo(),
                req.isFull())
            );
        } finally {
            MeasurementPoint.stop();
        }
    }

    private DetachOriginResultRO executeDetachOrigin(DetachOriginRequestRO req) {

        Objects.requireNonNull(req, "DetachOriginRequestRO can't be null");

        SplitRecordsDTO splitResult = dataRecordsService.detachOrigin(SplitRecordRequestContext.builder()
            .entityName(req.getEntityName())
            .externalId(Objects.nonNull(req.getOriginKey()) ? req.getOriginKey().getExternalId() : null)
            .sourceSystem(Objects.nonNull(req.getOriginKey()) ? req.getOriginKey().getSourceSystem() : null)
            .lsn(Objects.nonNull(req.getLsn()) ? req.getLsn().getLsn() : null)
            .shard(Objects.nonNull(req.getLsn()) ? req.getLsn().getShard() : null)
            .etalonKey(req.getEtalonId())
            .build());

        DetachOriginResultRO result = new DetachOriginResultRO();
        result.setEtalonId(splitResult.getEtalonId());
        result.setDetails(errorsToDetails(splitResult.getErrors()));
        return result;
    }

    public boolean isDeleteCascadeValue() {
        return deleteCascadeValue;
    }

    public void setDeleteCascadeValue(boolean deleteCascadeValue) {
        this.deleteCascadeValue = deleteCascadeValue;
    }
}
